<template>
  <div class="captcha">
    <div class="captcha__main" :style="imgWrapperStyle">
      <img v-if="src" :src="src" class="captcha_background" alt="background" ref="background" />
      <img v-if="sliderSrc" :src="sliderSrc" class="captcha_slider" alt="slider" ref="slider" :class="{ goFirst: isOk, goKeep: isKeep }" />
      <div class="captcha_message" v-if="showVerifyTip">
        <div class="captcha_message__icon">
          <svg v-if="isPassing" width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
            <g stroke="#fff" stroke-width="1.5" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
              <path d="M22.776 4.073A13.2 13.2 0 0 0 14 .75C6.682.75.75 6.682.75 14S6.682 27.25 14 27.25 27.25 21.318 27.25 14c0-.284-.009-.566-.027-.845"></path>
              <path d="M7 12.5l7 7 13-13"></path>
            </g>
          </svg>
          <svg v-else width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
            <g stroke="#fff" stroke-width="1.5" fill="none" fill-rule="evenodd">
              <circle cx="14" cy="14" r="13.25"></circle>
              <path stroke-linecap="round" stroke-linejoin="round" d="M8.75 8.75l10.5 10.5M19.25 8.75l-10.5 10.5"></path>
            </g>
          </svg>
        </div>
        <div class="captcha_message__text">
          {{ isPassing ? successTip : failTip }}
        </div>
      </div>
      <div class="captcha_message loadding" v-if="showGenerateLoadding">
        <div class="captcha_message__icon captcha_message__icon--loadding"></div>
        <div class="captcha_message__text">加载中...</div>
      </div>
      <div class="captcha_message" v-if="showVerifyLoadding">
        <div class="captcha_message__icon captcha_message__icon--loadding"></div>
        <div class="captcha_message__text"></div>
      </div>
    </div>
    <div class="captcha__bar" :style="dragVerifyStyle" ref="dragVerify">
      <div class="captcha_progress_bar" :class="{ goFirst2: isOk }" ref="progressBar" :style="progressBarStyle"></div>
      <div class="captcha_progress_bar__text" :style="textStyle">
        {{ text }}
      </div>
      <div class="captcha_handler" :class="{ goFirst: isOk }" :style="handlerStyle" ref="handler" @mousedown="handleDragStart" @touchstart="handleDragStart">
        <svg :style="handlerSvgStyle" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="819">
          <path
            d="M500.864 545.728a47.744 47.744 0 0 0 6.72-48.896 24.704 24.704 0 0 0-4.48-8.384L240.256 193.088a34.24 34.24 0 0 0-28.608-17.408 34.24 34.24 0 0 0-25.856 12.864 46.592 46.592 0 0 0 0 59.52l238.08 264.512-238.08 264.512a46.592 46.592 0 0 0-1.088 59.52 32 32 0 0 0 50.56 0l265.6-290.88z"
            p-id="820"
          ></path>
          <path
            d="M523.84 248.064l236.992 264.512-238.08 264.512a46.592 46.592 0 0 0 0 59.52 32 32 0 0 0 50.56 0l265.6-292.608a47.744 47.744 0 0 0 6.72-48.832 24.704 24.704 0 0 0-4.48-8.448L578.304 191.36a34.24 34.24 0 0 0-55.552-2.816 46.592 46.592 0 0 0 1.088 59.52z"
            p-id="821"
          ></path>
        </svg>
      </div>
    </div>
    <div class="captcha__actions" v-if="showRefresh">
      <a class="captcha__action" @click="handleRefresh">
        <svg fill="#505050" width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg">
          <path
            d="M10,4 C12.0559549,4 13.9131832,5.04358655 15.0015086,6.68322231 L15,5.5 C15,5.22385763 15.2238576,5 15.5,5 C15.7761424,5 16,5.22385763 16,5.5 L16,8.5 C16,8.77614237 15.7761424,9 15.5,9 L12.5,9 C12.2238576,9 12,8.77614237 12,8.5 C12,8.22385763 12.2238576,8 12.5,8 L14.5842317,8.00000341 C13.7999308,6.20218044 12.0143541,5 10,5 C7.23857625,5 5,7.23857625 5,10 C5,12.7614237 7.23857625,15 10,15 C11.749756,15 13.3431487,14.0944653 14.2500463,12.6352662 C14.3958113,12.4007302 14.7041063,12.328767 14.9386423,12.4745321 C15.1731784,12.6202971 15.2451415,12.9285921 15.0993765,13.1631281 C14.0118542,14.9129524 12.0990688,16 10,16 C6.6862915,16 4,13.3137085 4,10 C4,6.6862915 6.6862915,4 10,4 Z"
            fill-rule="nonzero"
          ></path>
        </svg>
        <span class="captcha__action__text">刷新</span>
      </a>
    </div>
  </div>
</template>

<script>
export default {
  name: "LazySlideCaptcha",
  props: {
    width: {
      type: Number,
      default: 340
    },
    height: {
      type: Number,
      default: 212
    },
    barHeight: {
      type: Number,
      default: 40
    },
    handlerIconWidth: {
      type: Number,
      default: 16
    },
    handlerIconHeigth: {
      type: Number,
      default: 16
    },
    background: {
      type: String,
      default: "#eee"
    },
    circle: {
      type: Boolean,
      default: false
    },
    radius: {
      type: String,
      default: "4px"
    },
    text: {
      type: String,
      default: "按住滑块拖动"
    },
    progressBarBg: {
      type: String,
      default: "#76c61d"
    },
    successTip: {
      type: String,
      default: "验证通过，超过80%用户"
    },
    failTip: {
      type: String,
      default: "验证未通过，拖动滑块将悬浮图像正确合并"
    },
    showRefresh: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      isMoving: false,
      x: 0,
      y: 0,
      isOk: false,
      isKeep: false,
      isFinish: false,
      tracks: [],
      startSlidingTime: undefined,
      showVerifyTip: false,
      showVerifyLoadding: false,
      showGenerateLoadding: false,
      src: "",
      sliderSrc: ""
    };
  },
  mounted() {
    const dragEl = this.$refs.dragVerify;
    dragEl.style.setProperty("--textColor", "#333");
    dragEl.style.setProperty("--width", Math.floor(this.width / 2) + "px");
    dragEl.style.setProperty("--pwidth", -Math.floor(this.width / 2) + "px");
  },
  destroyed() {
    this.removeEventListeners();
  },
  deactivated() {
    this.removeEventListeners();
  },
  computed: {
    imgWrapperStyle: function () {
      return {
        width: this.width + "px",
        height: this.height + "px",
        position: "relative",
        overflow: "hidden"
      };
    },
    dragVerifyStyle: function () {
      return {
        width: this.width + "px",
        height: this.barHeight + "px",
        lineHeight: this.barHeight + "px",
        background: this.background,
        borderRadius: this.circle ? this.barHeight / 2 + "px" : this.radius
      };
    },
    progressBarStyle: function () {
      return {
        background: this.progressBarBg,
        height: this.barHeight + "px",
        borderRadius: this.circle ? this.barHeight / 2 + "px 0 0 " + this.barHeight / 2 + "px" : this.radius
      };
    },
    textStyle: function () {
      return {
        height: this.barHeight + "px",
        width: this.width + "px",
        fontSize: this.textSize
      };
    },
    handlerStyle: function () {
      return {
        width: this.barHeight + "px",
        height: this.barHeight - 2 + "px",
        background: this.handlerBg
      };
    },
    handlerSvgStyle: function () {
      return {
        width: this.handlerIconWidth + "px",
        height: this.handlerIconHeigth + "px"
      };
    }
  },
  methods: {
    // 开始请求生成图片时调用
    startRequestGenerate() {
      this.reset();
      this.showGenerateLoadding = true;
    },
    // 结束请求生成图片时调用
    endRequestGenerate(src, sliderSrc) {
      this.showGenerateLoadding = false;
      this.src = src;
      this.sliderSrc = sliderSrc;
    },
    // 开始请求校验时调用
    startRequestVerify() {
      this.showVerifyLoadding = true;
    },
    // 结束请求校验时调用
    endRequestVerify(isPassing) {
      this.isPassing = isPassing;
      this.showVerifyLoadding = false;
      this.showVerifyTip = true;
    },
    reset() {
      this.x = 0;
      this.y = 0;
      this.tracks = [];
      this.isMoving = false;
      this.isFinish = false;
      this.showGenerateLoadding = false;
      this.showVerifyLoadding = false;
      this.showVerifyTip = false;
      this.isPassing = false;

      if (this.$refs.progressBar) this.$refs.progressBar.style.width = 0;
      if (this.$refs.slider) this.$refs.slider.style.left = 0;
      if (this.$refs.handler) this.$refs.handler.style.left = 0;
    },
    removeEventListeners() {
      window.removeEventListener("touchmove", this.handleDragMoving);
      window.removeEventListener("touchend", this.handleDragFinish);
      window.removeEventListener("mousemove", this.handleDragMoving);
      window.removeEventListener("mouseup", this.handleDragFinish);
    },
    handleDragStart(e) {
      if (!this.isPassing && this.src && this.sliderSrc && !this.isFinish) {
        window.addEventListener("touchmove", this.handleDragMoving);
        window.addEventListener("touchend", this.handleDragFinish);
        window.addEventListener("mousemove", this.handleDragMoving);
        window.addEventListener("mouseup", this.handleDragFinish);

        this.isMoving = true;
        this.startSlidingTime = new Date();
        this.x = e.pageX || e.touches[0].pageX;
        this.y = e.pageY || e.touches[0].pageY;
      }
    },
    handleDragMoving(e) {
      if (this.isMoving && !this.isPassing && this.src && this.sliderSrc && !this.isFinish) {
        var _x = (e.pageX || e.touches[0].pageX) - this.x;
        var _y = (e.pageY || e.touches[0].pageY) - this.y;

        this.$refs.handler.style.left = _x + "px";
        this.$refs.progressBar.style.width = _x + this.barHeight / 2 + "px";
        this.$refs.slider.style.left = _x + "px";

        this.tracks.push({
          x: Math.round(_x),
          y: Math.round(_y),
          t: new Date().getTime() - this.startSlidingTime.getTime()
        });
      }
    },
    handleDragFinish() {
      if (this.isMoving && !this.isPassing && this.src && this.sliderSrc && !this.isFinish) {
        this.isMoving = false;
        this.isFinish = true;
        this.removeEventListeners();
        this.$emit("finish", {
          backgroundImageWidth: this.$refs.background.offsetWidth,
          backgroundImageHeight: this.$refs.background.offsetHeight,
          sliderImageWidth: this.$refs.slider.offsetWidth,
          sliderImageHeight: this.$refs.slider.offsetHeight,
          startTime: this.startSlidingTime,
          endTime: new Date(),
          tracks: this.tracks
        });
      }
    },
    handleRefresh() {
      this.reset();
      this.$emit("refresh");
    }
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.captcha {
  user-select: none;
}

.captcha__main {
  background: rgb(244, 245, 246);
}

.captcha_background {
  width: 100%;
}

.captcha_slider {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  height: 100%;
}

.captcha_message {
  position: absolute;
  left: 0px;
  top: 0px;
  width: 100%;
  height: 100%;
  z-index: 999999;
  background-color: rgba(34, 34, 34, 0.85);
  display: flex;
  -webkit-box-pack: center;
  justify-content: center;
  -webkit-box-align: center;
  align-items: center;
  flex-direction: column;
}

.captcha_message__icon {
  width: 28px;
  height: 28px;
  margin: 0px auto;
}

.captcha_message__icon--loadding {
  border-radius: 50%;
  width: 24px;
  height: 24px;
  animation: 1s linear 0s infinite normal none running turn;
  background-image: url();
  background-size: contain;
  background-position: center center;
  background-repeat: no-repeat;
}

.captcha_message.loadding {
  background-color: rgb(244 245 246);
}

.captcha_message__text {
  padding: 10px;
  color: rgb(255, 255, 255);
  display: inline-block;
  text-align: center;
  max-width: 200px;
  font-size: 14px;
}

.captcha_message.loadding .captcha_message__text {
  color: rgb(202, 202, 202);
}

.captcha__bar {
  position: relative;
  text-align: center;
  overflow: hidden;
  width: 100%;
  margin-top: 5px;
}

.captcha_progress_bar {
  position: absolute;
  width: 0;
}

.captcha_progress_bar__text {
  position: absolute;
  top: 0px;
  width: 100%;
  font-size: 12px;
  color: transparent;
  -moz-user-select: none;
  -webkit-user-select: none;
  user-select: none;
  -o-user-select: none;
  -ms-user-select: none;
  background: -webkit-gradient(
    linear,
    left top,
    right top,
    color-stop(0, var(--textColor)),
    color-stop(0.4, var(--textColor)),
    color-stop(0.5, #fff),
    color-stop(0.6, var(--textColor)),
    color-stop(1, var(--textColor))
  );
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  -webkit-text-size-adjust: none;
  animation: slidetounlock 3s infinite;
}

.captcha_handler {
  position: absolute;
  top: 0px;
  left: 0px;
  cursor: move;
  background: rgb(255, 255, 255);
  margin: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.captcha__actions {
  padding: 16px 20px 20px 0px;
  display: flex;
  -webkit-box-pack: justify;
  justify-content: space-between;
  -webkit-box-align: center;
  align-items: center;
  line-height: 20px;
  min-height: 20px;
  color: rgb(80, 80, 80);
}

.captcha__action__text {
  color: rgb(80, 80, 80);
  font-size: 14px !important;
}

.captcha__action {
  display: flex;
  align-items: center;
  cursor: pointer;
  text-decoration: none;
}

.goFirst {
  left: 0px !important;
  transition: left 0.5s;
}
.goKeep {
  transition: left 0.2s;
}
.goFirst2 {
  width: 0px !important;
  transition: width 0.5s;
}

@-webkit-keyframes slidetounlock {
  0% {
    background-position: var(--pwidth) 0;
  }
  100% {
    background-position: var(--width) 0;
  }
}
@-webkit-keyframes slidetounlock2 {
  0% {
    background-position: var(--pwidth) 0;
  }
  100% {
    background-position: var(--pwidth) 0;
  }
}

@keyframes turn {
  0% {
    -webkit-transform: rotate(0deg);
  }
  25% {
    -webkit-transform: rotate(90deg);
  }
  50% {
    -webkit-transform: rotate(180deg);
  }
  75% {
    -webkit-transform: rotate(270deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
  }
}
</style>
